#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <stdbool.h>
#include <stdio.h>
#include <sys/socket.h>
#include <iostream>
#include "multi.pb.h"
#include "hmac.h"
#include <openssl/hmac.h>

#include <strings.h>
#include <arpa/inet.h>
#include <sys/sysinfo.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <google/protobuf/message.h>
#include <boost/filesystem.hpp>
#include <netinet/in.h>
#include <openssl/hmac.h>
#include <netinet/ip.h>



#include "udp_receiver.h"


	udp_receiver::udp_receiver(std::string const& multicastAddrString, int port)
	{
/*
  struct addrinfo addrCriteria;                   // Criteria for address match
  memset(&addrCriteria, 0, sizeof(addrCriteria)); // Zero out structure
  addrCriteria.ai_family = AF_UNSPEC;             // v4 or v6 is OK
  addrCriteria.ai_socktype = SOCK_DGRAM;          // Only datagram sockets
  addrCriteria.ai_protocol = IPPROTO_UDP;         // Only UDP protocol
  addrCriteria.ai_flags |= AI_NUMERICHOST;        // Don't try to resolve address

  // Get address information
  struct addrinfo *multicastAddr;                 // List of server addresses
  int rtnVal = getaddrinfo("224.0.0.1", "3333", &addrCriteria, &multicastAddr);
  if (rtnVal != 0)
    std::cout << "getaddrinfo() failed" << std::endl;

  // Create socket to receive on
  sock = socket(multicastAddr->ai_family, multicastAddr->ai_socktype, multicastAddr->ai_protocol);

  if (sock < 0)
    std::cout << "socket() failed" << std::endl;

  if (bind(sock, multicastAddr->ai_addr, multicastAddr->ai_addrlen) < 0)
    std::cout << "bind() failed" << std::endl;

  // Unfortunately we need some address-family-specific pieces
  if (multicastAddr->ai_family == AF_INET6) {
    // Now join the multicast "group" (address)
    struct ipv6_mreq joinRequest;
    memcpy(&joinRequest.ipv6mr_multiaddr, &((struct sockaddr_in6 *)
	   multicastAddr->ai_addr)->sin6_addr,  sizeof(struct in6_addr));
    joinRequest.ipv6mr_interface = 0;   // Let system choose the i/f
    std::cout << "Joining IPv6 multicast group..." << std::endl;
    if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
		   &joinRequest, sizeof(joinRequest)) < 0)
      std::cout << "setsockopt(IPV6_JOIN_GROUP) failed" << std::endl;
  } else if (multicastAddr->ai_family == AF_INET) {
    // Now join the multicast "group"
    struct ip_mreq joinRequest;
    joinRequest.imr_multiaddr =
      ((struct sockaddr_in *) multicastAddr->ai_addr)->sin_addr;
    joinRequest.imr_interface.s_addr = 0;  // Let the system choose the i/f
    std::cout << "Joining IPv4 multicast group...\n" << std::endl;
    if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
		   &joinRequest, sizeof(joinRequest)) < 0)
      std::cout << "setsockopt(IPV4_ADD_MEMBERSHIP) failed" << std::endl;
  } else {
    std::cout << "Unknown address family" << std::endl;
  }
  freeaddrinfo(multicastAddr);
*/
   int n;
   struct sockaddr_in servaddr,cliaddr;

   sock=socket(AF_INET,SOCK_DGRAM,0);

   bzero(&servaddr,sizeof(servaddr));
   servaddr.sin_family = AF_INET;
   servaddr.sin_addr.s_addr=inet_addr(multicastAddrString.c_str());
   servaddr.sin_port=htons(port);
   bind(sock,(struct sockaddr *)&servaddr,sizeof(servaddr));

	}


server::Block udp_receiver::getBlock(std::string &ip)
        {
                while(1)
                {
                        socklen_t len;
                        char mesg[10000];
			struct sockaddr m_cliaddr;
                        len = sizeof(m_cliaddr);
                        int byte_count = recvfrom(sock,mesg,10000,0,(struct sockaddr *)&m_cliaddr,&len);
                        std::string data(mesg,byte_count);

                        if(data.size() >20) {
                                std::string hmacStr = data.substr(data.size() - 20,20);
                                std::string blockData = data.substr(0,data.size() - 20);

                                if(hmacStr == hmac(blockData)) {
                                        server::Block block;
                                        block.ParseFromString(blockData);
                                        return block;
                                }
                        }
                }
        }

	udp_receiver::~udp_receiver()
	{
  	close(sock);

	}

/*
int main(int argc, char *argv[]) 
{

	udp_receiver skod(MULTICASTIP, CLIENT_MULTICASTPORT);

	while(1)
	{
		std::string ip;
		server::Block ok(skod.getBlock(ip));
	
		std::cout << ok.master().time() << std::endl;
	}
}*/
